package com.topc.oj.language.detail;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.topc.base.utils.LogUtils;
import com.topc.oj.constant.OJConstant;
import com.topc.oj.enums.CodeStatusEnum;
import com.topc.oj.language.LanguageCodeDo;
import com.topc.oj.result.CodeCompileResult;
import com.topc.oj.result.CodeRunResult;
import com.topc.oj.runnable.code.CodeRunnable;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.*;

public class CppCodeDo implements LanguageCodeDo {

    private ProcessBuilder pb;

    private final static String CLASS_NAME = "main";

    private final String taskId = UUID.fastUUID().toString();

    @Override
    public CodeStatusEnum checkOutputStream(String s) {
        if (ObjectUtil.isEmpty(s)) return CodeStatusEnum.Finish;
        // 判断是否超时
        if (s.contains("Time Limit Exceeded")
                || s.contains("Execution Timed Out")
                || s.contains("Maximum Execution Time Exceeded")
                || s.contains("Time Limit Reached")
                || s.contains("Program Timed Out")
              ) {
            return CodeStatusEnum.TLE;
        }

        // 判断是否内存超限
        if (s.contains("Out of Memory")
                || s.contains("Memory Limit Exceeded")
                || s.contains("Segmentation Fault (core dumped)")
                || s.contains("Stack Overflow")) {
            return CodeStatusEnum.MLE;
        }
        if (s.contains("too large for field")) return CodeStatusEnum.MLE;
        return CodeStatusEnum.Finish;
    }

    @Override
    public String taskId() {
        return taskId;
    }

    @Override
    public String fileSuffix() {
        return ".cpp";
    }

    @Override
    public String className() {
        return CLASS_NAME;
    }

    @Override
    public CodeCompileResult runCompile(String code) {
        pb = new ProcessBuilder("g++","-o",OJConstant.SAVE_FILE_PATH+taskId+"\\output.exe",OJConstant.SAVE_FILE_PATH+taskId+"\\"+CLASS_NAME+fileSuffix());
        try {
            Process p = pb.start();
            p.waitFor();
            if (p.exitValue() != 0) {
                InputStream errorStream = p.getErrorStream();
                byte[] buffer = new byte[1024];
                int length;
                StringBuilder errorInfo = new StringBuilder();
                while ((length = errorStream.read(buffer)) != -1) {
                    errorInfo.append(new String(buffer, 0, length));
                }
                String errorInfoS = errorInfo.toString();
                //LogUtils.getLog().error("编译报错 任务id{}",taskId);
                LogUtils.printChar("*",20);
                //System.out.println("报错内容");
                //System.out.println(errorInfoS);
                LogUtils.printChar("*",20);
                return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.COMPILE_ERROR).setErrorInfo(errorInfoS);
            }
        } catch (IOException | InterruptedException e) {
            //LogUtils.getLog().info("编译发生未知错误 任务id:{}",taskId);
            return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.UN_KNOW);
        }
        //LogUtils.getLog().info("成功 任务id:{}",taskId);
        return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.Finish);
    }

    @Override
    public CodeRunResult run(String input, int timeLimit, int memoryLimit) {
        pb = new ProcessBuilder(OJConstant.SAVE_FILE_PATH+taskId+"\\output.exe");
        ExecutorService executor = Executors.newSingleThreadExecutor();
        CodeRunnable codeRunnable = new CodeRunnable(pb, input, memoryLimit, timeLimit);
        Future<CodeRunResult> future = executor.submit(codeRunnable);
        CodeRunResult codeRunResult = new CodeRunResult();
        try {
            codeRunResult = future.get(timeLimit, TimeUnit.MILLISECONDS);
            CodeStatusEnum codeStatusEnum = checkOutputStream(codeRunResult.getContentInfo());
        } catch (TimeoutException e) {
            codeRunResult.setCodeStatusEnum(CodeStatusEnum.TLE);
        } catch (InterruptedException | ExecutionException e) {
            codeRunResult.setCodeStatusEnum(CodeStatusEnum.UN_KNOW);
        }finally {
            executor.shutdownNow();
        }
        return codeRunResult;
    }

}
